package com.yry.ams.util;

import cn.hutool.core.collection.CollectionUtil;
import com.yry.ams.ex.BaseErrorException;
import com.yry.ams.param.GrabVideoImgParam;
import com.yry.ams.param.ImgOCRParam;
import com.yry.ams.param.ImgToVideoParam;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.TesseractException;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.bytedeco.ffmpeg.global.avcodec;
import org.bytedeco.javacpp.BytePointer;
import org.bytedeco.javacv.*;
import org.bytedeco.leptonica.PIX;
import org.bytedeco.leptonica.global.lept;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.tesseract.TessBaseAPI;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.COLOR_BGRA2GRAY;
import static org.bytedeco.opencv.global.opencv_imgproc.cvtColor;

/**
 * ffmpeg和opencv是两套组件, 处理多媒体音视频流用ffmpeg   ,  要是处理视觉图像用opencv
 */
public class JavacvUtils {


    /**
     * 运动因子
     */
    private static final double MOTION_FACTOR = 1;

    /**
     * 图片合成 mp4 类型的视频
     * 建议合成的图片宽高要一致，并且视频的宽高还是要符合一定比例
     * @param param  视频地址
     * @throws FrameRecorder.Exception
     */
    public static String convertImgToMp4(ImgToVideoParam param) throws FrameRecorder.Exception, FrameGrabber.Exception {
        String fileNm = null ;
        if(StringUtils.isBlank(param.getVideoSavePath())){
            throw new BaseErrorException("文件存储地址 不能为空");
        }
        if(param.getVideoSavePath().endsWith(param.getFormat())){
            fileNm = param.getVideoSavePath();
        }else{
            if(param.getVideoSavePath().endsWith(File.separator)){
                fileNm = param.getVideoSavePath() + (StringUtils.isBlank(param.getVideoFileNm())? RandomStringUtils.randomAlphanumeric(7):param.getVideoFileNm()) +"."+param.getFormat();
            }else{
                fileNm = param.getVideoSavePath() +File.separator+ (StringUtils.isBlank(param.getVideoFileNm())? RandomStringUtils.randomAlphanumeric(7):param.getVideoFileNm()) +"."+param.getFormat();
            }
        }
        Map<Integer, File> imgMap = new HashMap<Integer, File>();
        List<String> imgUrlList = param.getImgList();
        if(CollectionUtil.isEmpty(imgUrlList)){
            //读取所有图片
            File file = new File(param.getImgPath());
            File[] files = file.listFiles();
            if(files==null||files.length<1){
                throw new BaseErrorException("对应路劲【"+param.getImgList()+"】下无文件");
            }
            int num = 0;
            for (File imgFile : files) {
                if(ImgCheck.getInstance().isImage(imgFile)){
                    imgMap.put(num, imgFile);
                    num++;
                }
            }
        }
        // 处理视频宽高
        formatWidthAndHeight(param.getImgWidth(),param.getImgHeight());
        // 帧 抓取器
        FFmpegFrameGrabber grabber = null;
        if(StringUtils.isNotEmpty(param.getMusicPath())){
            InputStream is = readFileInputStream(param.getMusicPath());
//          grabber =  FFmpegFrameGrabber.createDefault(param.getMusicPath());
//          grabber = new FFmpegFrameGrabber(musicFile);
//          grabber = new FFmpegFrameGrabber(param.getMusicPath());
            grabber = new FFmpegFrameGrabber(is);
        }
        //视频宽高最好是按照常见的视频的宽高  16：9  或者 9：16
        FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(param.getVideoSavePath(), param.getImgWidth(),param.getImgHeight());
        //设置视频编码层模式
        recorder.setVideoCodec(param.getVideoCodec());
        //设置视频为25帧每秒
        recorder.setFrameRate(param.getFrameRate());
        /*
        * videoBitRate这个参数很重要，当然越大，越清晰，但最终的生成的视频也越大。
        * 查看一个资料，说均衡考虑建议设为videoWidth*videoHeight*frameRate*0.07*运动因子，运动因子则与视频中画面活动频繁程度有关，如果很频繁就设为4，不频繁则设为1
        */
        recorder.setVideoBitrate((int)((param.getImgWidth()*param.getImgHeight()*param.getFrameRate())*param.getMotionFactory()*0.07));
        //设置视频图像数据格式
        recorder.setPixelFormat(param.getPixelFormat());
        recorder.setFormat(param.getFormat());
        // 不可变(固定)音频比特率
//        recorder.setAudioOption("crf", "0");
//        //最高质量
//        recorder.setAudioQuality(0);
//         音频采样率
//        recorder.setSampleRate(grabber.getSampleRate());
//        recorder.setSampleRate((int)FRAME_RATE);
//         双通道(立体声)
//        recorder.setAudioChannels(grabber.getAudioChannels());
        recorder.setAudioChannels(2);
//         音频比特率
//        recorder.setAudioBitrate(grabber.getAudioBitrate());
        // 告诉录制器这个audioSamples的音频时长
//        recorder.setTimestamp(grabber.getTimestamp());

        recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
        try {
            recorder.start();
            // 录制视频
            Java2DFrameConverter converter = new Java2DFrameConverter();
            if(CollectionUtil.isNotEmpty(imgUrlList)){
                if(param.isRateRepeatImg()) {
                    for (int i = 0, length = imgUrlList.size(); i < length; i++) {
                        URL imgUrl = new URL(imgUrlList.get(i));
                        BufferedImage read = ImageIO.read(imgUrl);
                        //根据每秒的帧数 记录多少次图片
                        for (int j = 0; j < param.getFrameRate(); j++) {
                            recorder.record(converter.getFrame(read));
                        }
                    }
                }else{
                    int imgUrlIndex = 0,length = imgUrlList.size();
                    //根据每秒的帧数 记录图片
                    while (imgUrlIndex<length){
                        for (int j = 0; j < param.getFrameRate(); j++) {
                            if(imgUrlIndex>=length){
                                break;
                            }
                            URL imgUrl = new URL(imgUrlList.get(imgUrlIndex));
                            BufferedImage read = ImageIO.read(imgUrl);
                            recorder.record(converter.getFrame(read));
                            ++imgUrlIndex;
                        }
                    }
                }
            }else{
                if(param.isRateRepeatImg()) {
                    for (int i = 0, length = imgMap.size(); i < length; i++) {
                        BufferedImage read = ImageIO.read(imgMap.get(i));
                        // 根据每秒的帧数 记录多少次图片
                        for (int j = 0; j < param.getFrameRate(); j++) {
                            recorder.record(converter.getFrame(read));
                        }
                    }
                }else{
                    int imgUrlIndex = 0,length = imgMap.size();
                    //根据每秒的帧数 记录图片
                    while (imgUrlIndex<length){
                        for (int j = 0; j < param.getFrameRate(); j++) {
                            if(imgUrlIndex>=length){
                                break;
                            }
                            BufferedImage read =  ImageIO.read(imgMap.get(imgUrlIndex));
                            recorder.record(converter.getFrame(read));
                            ++imgUrlIndex;
                        }
                    }
                }
            }
            if(grabber!=null){
                // 开始录制音频
                grabber.start();
                Frame audioSamples;
                while ((audioSamples = grabber.grabFrame()) != null) {
                    if(audioSamples.samples  != null){
                        recorder.record(audioSamples); // 录入音频
                    }
                }
            }
            return fileNm;
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseErrorException("视频合成出错： " + e.getMessage());
        } finally {
            if(grabber!=null){
                grabber.stop();
                grabber.release();
            }
            recorder.stop();
            recorder.release();
        }
    }



    /**
     *  获取视频帧转存图片
     * @param param
     * @return  图片名称集合
     */
    public static List<String> grabVideoImgSaveLocal(GrabVideoImgParam param) throws FFmpegFrameGrabber.Exception {
        //获取文件流
        InputStream fileInputStream = readFileInputStream(param.getFilePath());
        if(fileInputStream==null){
            throw new BaseErrorException("文件地址有误: "+param.getFilePath() );
        }
        String fileTargetName =readFileNm(param.getFilePath());
        // 帧抓取器
        FFmpegFrameGrabber fileGrabber = new FFmpegFrameGrabber(fileInputStream);

        List<String> files = new ArrayList<String>();
        try {
            fileGrabber.start();
            //获取总帧长度
            int ftp = fileGrabber.getLengthInFrames();
            Integer start=param.getStart();
            Integer end=param.getEnd();
            if(start==null||start<0){
                start = 0;
            }
            if(end==null||end<1){
                end = ftp;
            }
           //开始视频提取帧
            Frame frame ;
            for (int i=0 ; i < ftp ; i++){
                if( i >= start && i <= end){
                    //获取图片frame
                    frame = fileGrabber.grabImage();
                    //将图片frame 中的图片保存到本地
                    File picFile = doExecuteFrame(frame, param.getImgLocalSavePath(), fileTargetName, i,param.getImageMat() );
                    if(picFile!=null){
                        files.add(picFile.getPath());
                    }
                }
            }
            return files;
        } catch (FFmpegFrameGrabber.Exception e) {
            e.printStackTrace();
            throw new BaseErrorException("文件解析图片发生异常: "+e.getMessage());
        }finally {
            fileGrabber.stop();
            fileGrabber.release();
        }
    }

    /**
     * 图片文字识别
     * @param param
     * @return
     */
    public static String imgOCR(ImgOCRParam param) {
        //启动识别器
        TessBaseAPI api=new TessBaseAPI();
        //初始化识别器的语言包
        if (api.Init(param.getDataPath(), param.getLng())!=0){
            throw new BaseErrorException("语言数据集init出错");
//            return null;
        }


        //读取源图片
        Java2DFrameConverter converter = new Java2DFrameConverter();
        BufferedImage imgRead = FileUtils.readImgBuffer(param.getImagePath());

        // 图片做灰度处理
        BufferedImage grayImage = grayImage(imgRead);
        File grayImageFile=null;
        try{
            String grayImageFilePath = param.getGrayImagePath();
            if(StringUtils.isEmpty(grayImageFilePath)){
                File directory = new File("");// 参数为空
                grayImageFilePath = directory.getCanonicalPath()+File.separator+readFileNm(param.getImagePath())+".jpg";
            }
            grayImageFile= new File(grayImageFilePath);
            ImageIO.write(grayImage, "jpg", grayImageFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        if(grayImageFile==null||!grayImageFile.exists()){
            return null;
        }
        PIX image= lept.pixRead(grayImageFile.getPath());
        //识别器装入图片
        api.SetImage(image);

        //识别器识别进行段
        BytePointer outText=api.GetUTF8Text();

        try {
            //指定编码格式
            if(StringUtils.isNotEmpty(param.getEncode())){
                return outText.getString(param.getEncode());
            }
            return outText.getString();
//            BytePointer outText=api.GetUTF8Text();
//            return outText.getString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            return null;
        } finally {
            //最后释放资源
            api.End();
            if(outText!=null){
                outText.deallocate();
            }
            if(param.isDelgrayImage()){
                grayImageFile.delete();
            }
//            lept.pixDestroy(image);
        }
    }


    /**
     * 图片文字识别
     * @param param
     * @return
     */
    public static String imgOCR_2(ImgOCRParam param) throws TesseractException {
        ITesseract instance = new Tesseract();
        //如果未将tessdata放在根目录下需要指定绝对路径
        //设置训练库的位置
        instance.setDatapath(param.getDataPath());

        //如果需要识别英文之外的语种，需要指定识别语种，并且需要将对应的语言包放进项目中
        // chi_sim ：简体中文， eng    根据需求选择语言库
        instance.setLanguage("chi_sim");

        // 指定识别图片
        BufferedImage imgRead = FileUtils.readImgBuffer(param.getImagePath());
        long startTime = System.currentTimeMillis();
        return instance.doOCR(imgRead);
    }



    /**
     *  视频的宽度必须是32的倍数，高度必须是2的倍数
     * @param width
     * @param height
     */
    private static void formatWidthAndHeight(int width,int height){
        if (width % 32 != 0) {
            int j = width % 32;
            if (j <= 16) {
                width = width - (width % 32);
            } else {
                width = width + (32 - width / 32);
            }
        }
        if (height % 2 != 0) {
            int j = height % 4;
            switch (j) {
                case 1:
                    height = height - 1;
                    break;
                case 3:
                    height = height + 1;
                    break;
            }
        }
    }

    /**
     * 读取文件 输入流
     * @param filepatah  文件绝对路劲
     * @return
     */
    private static InputStream readFileInputStream(String filepatah){
        InputStream is = null;
        if(StringUtils.isEmpty(filepatah)){
            return is;
        }
        File file = new File(filepatah);
        try {
            if(file!=null&&file.exists()){
                is = new FileInputStream(file);
            }else{
                URL musicUrl = new URL(filepatah);
                HttpURLConnection conn= (HttpURLConnection)musicUrl.openConnection();
                //设置超时间为3秒
                conn.setConnectTimeout(3*1000);
                //防止屏蔽程序抓取而返回403错误
                conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
//                    conn.connect();
                is = conn.getInputStream();
            }
        }catch (IOException e) {
            e.printStackTrace();
            throw new BaseErrorException("读取文件【"+filepatah+"】I/O异常：  "+ e.getMessage());
        }
        return is;
    }

    public static byte[] readBytes3(InputStream in) throws IOException {
        BufferedInputStream bufin = new BufferedInputStream(in);
        int buffSize = 1024;
        ByteArrayOutputStream out = new ByteArrayOutputStream(buffSize);
        byte[] temp = new byte[buffSize];
        int size = 0;
        while ((size = bufin.read(temp)) != -1) {
            out.write(temp, 0, size);
        }
        bufin.close();

        byte[] content = out.toByteArray();
        return content;
    }

    /**
     * 保存Frame对应的图片到本地
     * @param frame
     * @param targetFilePath  图片本地存储路劲
     * @param targetFileName  图片名称
     * @param index
     * @param imageMat         图片格式后缀，默认jpg
     */
    private static File doExecuteFrame(Frame frame, String targetFilePath, String targetFileName, int index,String imageMat) {
        if ( frame == null || frame.image == null) {
            return null;
        }
        Java2DFrameConverter converter = new Java2DFrameConverter();
        if(StringUtils.isEmpty(imageMat)){
            imageMat = "jpg";
        }
        String folderUrl = (targetFilePath.endsWith(File.separator)?targetFilePath:targetFilePath+File.separator)+targetFileName;
        File folder = new File(folderUrl);
        if(!folder.exists()){
            folder.mkdir();//创建文件夹
        }
        String fileName = folderUrl +File.separator+ targetFileName + "_" + index + "." + imageMat;
        BufferedImage bi = converter.getBufferedImage(frame);
        File output = new File(fileName);
        try{
            ImageIO.write(bi, imageMat, output);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return output;
    }

    /**
     * 根据文件路劲获取文件名称
     * @param filePath
     * @return
     */
    public static String readFileNm(String filePath){
        String fName = filePath.trim();
        String[] temp = fName.split("\\\\"); /**split里面必须是正则表达式，"\\"的作用是对字符串转义*/
        if(temp.length==1){
            fName = temp[0];
            temp =  fName.split("\\/");
        }
        String fileNm=temp[temp.length-1];
        int dot = fileNm.lastIndexOf('.');
        if ((dot >-1) && (dot < (fileNm.length() - 1))) {
            return fileNm.substring(0,dot);
        }
        return fileNm;
    }


    public static BufferedImage grayImage(BufferedImage img){
        int imgWidth = img.getWidth();
        int imgHeight = img.getHeight();
        BufferedImage grayImage = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_BYTE_GRAY);
        for(int i= 0 ; i < imgWidth ; i++){
            for(int j = 0 ; j < imgHeight; j++){
                int rgb = img.getRGB(i, j);
                grayImage.setRGB(i, j, rgb);
            }
        }
        return grayImage;
    }

}
